using System;
using System.Text;

using Tao.OpenGl;

namespace OhioState.Graphics.OpenGL
{
    public class VertexRoutineGL : ShaderRoutineGL
    {
        public VertexRoutineGL()
        {
            shaderType = Gl.GL_VERTEX_SHADER;
        }
    }

    public class FragmentRoutineGL : ShaderRoutineGL
    {
        public FragmentRoutineGL()
        {
            shaderType = Gl.GL_FRAGMENT_SHADER;
        }
    }

    public class ShaderRoutineGL : IShaderRoutine, IOpenGLResource, IDisposable
    {
        #region Public Properties
        public bool IsCompiled
        {
            get { return isCompiled; }
        }
        #endregion

        #region Constructors
        protected ShaderRoutineGL()
        {
        }
        #endregion

        #region IShaderRoutine Members
        // step 3: compile source code for routine
        public bool Compile(IRenderPanel panel)
        {
            if (!created)
            {
                Create();
            }
            if (needsCompiled)
            {
                LoadSource();
                // Create the Handle (GUID) for the shader
                Gl.glCompileShader(guid);
                int compileStatus;
                Gl.glGetShaderiv(guid, Gl.GL_COMPILE_STATUS, out compileStatus);
                isCompiled = (compileStatus == Gl.GL_TRUE);
                SetCompilerLog();
                needsCompiled = false;
            }
            return isCompiled;
        }

        public string CompileLog
        {
            get { return compileLog.ToString(); }
        }

        public string ShaderContent
        {
            get
            {
                return currentContent[0];
            }
            set
            {
                currentContent[0] = value;
                needsCompiled = true;
            }
        }

        #endregion

        #region IOpenGLResource Members
        public uint GUID
        {
            get { return guid; }
        }
        #endregion

        #region IDisposable Members
        public void Dispose()
        {
            Dispose(true);
        }
        private void Dispose(bool disposing)
        {
            if (disposing)
            {
            }
            if (created)
            {
                Gl.glDeleteShader(guid);
            }
            GC.SuppressFinalize(this);
        }

        #endregion

        #region Implementation
        private void Create()
        {
            // Since OpenGl wants to return an unsigned int and unsigned int's
            // are not CLR compliant, we need to cast this. ATI was giving me
            // some signed numbers, so we need to prevent a conversion here.
            unchecked
            {
                // Create the Handle (GUID) for the shader
                guid = (uint)Gl.glCreateShader(shaderType);
            }
            created = true;
        }

        // step 2: load source code for routine 
        private void LoadSource()
        {
            int length = currentContent[0].Length;
            string[] content = new string[1];
            int[] lengthArray = { length };
            // load the source code into the shader object
            Gl.glShaderSource(guid, 1, currentContent, lengthArray);
        }

        private void SetCompilerLog()
        {
            int maxLength;
            Gl.glGetShaderiv(guid, Gl.GL_INFO_LOG_LENGTH, out maxLength);
            compileLog.EnsureCapacity(maxLength);
            Gl.glGetShaderInfoLog(guid, maxLength, out maxLength, compileLog);
        }
        #endregion

        #region Member variables
        private uint guid = 0;
        protected int shaderType = Gl.GL_VERTEX_SHADER;
        string[] currentContent = new string[1];
        private bool isCompiled = false;
        private bool created = false;
        private bool needsCompiled = false;
        private StringBuilder compileLog = new StringBuilder(1024);
        #endregion
    }
}
